package com.ymt.bpm.service.platform.fauth.impl;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.ymt.bpm.appboot.env.Caches;
import com.ymt.bpm.dao.MDao;
import com.ymt.bpm.dao.Mapper;
import com.ymt.bpm.model.Const.Oplog;
import com.ymt.bpm.model.PResourceRole;
import com.ymt.bpm.model.PRole;
import com.ymt.bpm.model.PRoleMember;
import com.ymt.bpm.model.PRoleUser;
import com.ymt.bpm.service.platform.cache.CacheService;
import com.ymt.bpm.service.platform.fauth.FaRoleService;
import com.ymt.bpm.service.platform.log.OpLogService;
import com.ymt.bpm.util.Const;
import com.ymt.bpm.util.DateUtil;
import com.ymt.bpm.util.DhxUtil;
import com.ymt.bpm.util.Platform;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.*;

/**
 * Created by Johnny on 2017/3/19.
 */
@Service
public class FaRoleServiceImpl implements FaRoleService {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private MDao mDao;

    @Autowired
    private OpLogService oplog;

    @Autowired
    private CacheService cacheService;

    @Override
    public JSONObject query(String tenantId, String roleType, String search, String sort,
                            String order, int offset, int limit, int timezoneOffset) {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put(Const.TENANT_ID, tenantId);
        paramMap.put("roleType", roleType);
        paramMap.put("search", search);
        paramMap.put("sort", sort);
        paramMap.put("order", order);
        PageHelper.offsetPage(offset, limit);
        List<PRole> list = mDao.selectList(Mapper.PRoleMapper, "query", paramMap);
        PageInfo pageInfo = new PageInfo(list);
        JSONObject rtn = new JSONObject();
        JSONArray rows = new JSONArray();
        if (list!=null && list.size()>0) {
            for (PRole role : list) {
                JSONObject node = DhxUtil.toJsonWithDateAli(role, Const.LANG_ZH, timezoneOffset);
                node.put("_ITEM_NO", ++offset);
                rows.add(node);
            }
        }
        rtn.put("rows", rows);
        rtn.put("total", pageInfo.getTotal());
        return rtn;
    }

    @Override
    public JSONObject get(String tenantId, String orgId, String roleId, String lang, int timezoneOffset) {
        PRole role = new PRole();
        role.setTenantId(tenantId);
        role.setOrgId(orgId);
        role.setRoleId(roleId);
        role = mDao.selectOne(Mapper.PRoleMapper, "selectByObj", role);
        JSONObject rtn = DhxUtil.toFastJsonWithDate(role, lang, timezoneOffset);
        return rtn;
    }

    @Override
    public JSONObject delete(String tenantId, String loginName, String displayName, String orgId, String roleId) {
        //TODO check references

        //更新缓存
        Set<String> logins = new HashSet<String>();
        getAffectedUsers(tenantId, roleId, logins);
        evictMenuCacheForUsers(tenantId, logins);

        PRole role = new PRole();
        role.setTenantId(tenantId);
        role.setOrgId(orgId);
        role.setRoleId(roleId);
        role = mDao.selectOne(Mapper.PRoleMapper, "selectByObj", role);

        int ret = mDao.delete(Mapper.PRoleMapper, "deleteByObj", role);
        JSONObject rtn = new JSONObject();
        rtn.put("result", ret);

        //记录日志
        oplog.bizlog(tenantId, loginName, displayName, null, Oplog.OP.DELETE, Oplog.CODE.ROLE, role.getRoleName());

        return rtn;
    }

    @Override
    public String checkAttr(String tenantId, String attrName, String value, String roleId) {
        if (log.isDebugEnabled()) {
            log.debug("checkAttr tenantId="+tenantId);
            log.debug("checkAttr attrName="+attrName);
            log.debug("checkAttr value="+value);
            log.debug("checkAttr roleId="+roleId);
        }
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("tenantId", tenantId);
        paramMap.put("value", value);
        paramMap.put("roleId", roleId);
        int rs = mDao.selectOne(Mapper.PRoleMapper, "checkAttr"+ StringUtils.capitalize(attrName), paramMap);
        if (log.isDebugEnabled()) {
            log.debug("checkAttr rs="+rs);
        }
        return rs>0 ? "false" : "true";
    }

    @Override
    public JSONObject save(String tenantId, String loginName, String displayName, PRole role) {
        int ret = 0;
        if (role.getRoleId()==null || role.getRoleId().length()==0) {
            role.setTenantId(tenantId);
            role.setRoleId("ROLE-"+ UUID.randomUUID().toString());
            role.setCreatorId(loginName);
            role.setCreator(displayName);
            role.setCreateTime(DateUtil.getGMTDate());
            ret = mDao.insert(Mapper.PRoleMapper, "insertSelective", role);

            //记录日志
            oplog.bizlog(tenantId, loginName, displayName, null, Oplog.OP.CREATE, Oplog.CODE.ROLE, role.getRoleName());
        } else {
            role.setUpdatorId(loginName);
            role.setUpdator(displayName);
            role.setUpdateTime(DateUtil.getGMTDate());
            ret = mDao.update(Mapper.PRoleMapper, "updateByPrimaryKeySelective", role);

            //记录日志
            oplog.bizlog(tenantId, loginName, displayName, null, Oplog.OP.UPDATE, Oplog.CODE.ROLE, role.getRoleName());
        }

        JSONObject rtn = new JSONObject();
        rtn.put("result", ret);
        return rtn;
    }

    @Override
    public JSONArray getGrant(String tenantId, String roleId, String resControl) {
        JSONArray rtn = new JSONArray();
        PResourceRole rr = new PResourceRole();
        rr.setTenantId(tenantId);
        rr.setRoleId(roleId);
        rr.setResControl(resControl);
        List<PResourceRole> rrs = mDao.selectListByObj(Mapper.PResourceRoleMapper, "selectGrant", rr);
        if (rrs!=null && rrs.size()>0) {
            for (PResourceRole r : rrs) {
                rtn.add(r.getResId());
            }
        }
        return rtn;
    }

    @Override
    public JSONObject saveGrant(String tenantId, String loginName, String displayName,
                                String roleId, String resIds, String resControl) {
        int ret = 0;
        //delete old granting
        PResourceRole rr = new PResourceRole();
        rr.setTenantId(tenantId);
        rr.setRoleId(roleId);
        rr.setResControl(resControl);
        ret += mDao.delete(Mapper.PResourceRoleMapper, "deleteGrant", rr);

        JSONArray ja = JSONArray.parseArray(resIds);
        if (ja!=null && ja.size()>0){
            List<PResourceRole> paramList = new ArrayList<PResourceRole>();
            for (int i=0; i<ja.size(); i++) {
                PResourceRole rr1 = new PResourceRole();
                String resId = ja.getString(i);
                rr1.setTenantId(tenantId);
                rr1.setRoleId(roleId);
                rr1.setResControl(resControl);
                rr1.setResId(resId);
                paramList.add(rr1);
            }
            ret += mDao.batchInsert(Mapper.PResourceRoleMapper, "insertGrant", paramList);
        }

        PRole role = new PRole();
        role.setTenantId(tenantId);
        role.setRoleId(roleId);
        role = mDao.selectOne(Mapper.PRoleMapper, "selectByObj", role);
        role.setUpdatorId(loginName);
        role.setUpdator(displayName);
        role.setUpdateTime(DateUtil.getGMTDate());
        ret += mDao.update(Mapper.PRoleMapper, "updateByPrimaryKeySelective", role);

        JSONObject rtn = new JSONObject();
        rtn.put("result", ret);

        //更新缓存
        Set<String> logins = new HashSet<String>();
        getAffectedUsers(tenantId, roleId, logins);
        evictMenuCacheForUsers(tenantId, logins);

        //记录日志
        oplog.bizlog(tenantId, loginName, displayName, null, Oplog.OP.CHANGEGRANT, Oplog.CODE.ROLE, role.getRoleName());

        return rtn;
    }

    @Override
    public JSONArray getMember(String tenantId, String roleId, String memberType) {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put(Const.TENANT_ID, tenantId);
        paramMap.put("roleId", roleId);
        List<Map<String, Object>> rms = mDao.selectList(Mapper.PRoleMemberMapper, "selectMember", paramMap);
        JSONArray rtn = new JSONArray();
        if (rms!=null && rms.size()>0) {
            for (Map<String, Object> r : rms) {
                rtn.add(r);
            }
        }
        return rtn;
    }

    @Override
    public JSONObject saveMember(String tenantId, String loginName, String displayName, String roleId, String members) {
        JSONObject rtn = new JSONObject();
        int ret = 0;

        //更新缓存需要查询更新前用户成员
        Set<String> logins = new HashSet<String>();
        getAffectedUsers(tenantId, roleId, logins);

        //delete existing member
        PRoleMember rm1 = new PRoleMember();
        rm1.setTenantId(tenantId);
        rm1.setRoleId(roleId);
        ret += mDao.delete(Mapper.PRoleMemberMapper, "deleteByObj", rm1);

        //add member
        JSONArray mja = JSONArray.parseArray(members);
        if (mja!=null && mja.size()>0) {
            List<PRoleMember> listToAdd = new ArrayList<PRoleMember>();
            for (int i=0; i<mja.size(); i++) {
                JSONObject m = mja.getJSONObject(i);
                PRoleMember rm = new PRoleMember();
                rm.setTenantId(tenantId);
                rm.setRoleId(roleId);
                rm.setMemberType(m.getString("MEMBER_TYPE"));
                rm.setMemberId(m.getString("MEMBER_ID"));
                listToAdd.add(rm);
            }
            ret += mDao.batchInsert(Mapper.PRoleMemberMapper, "insertSelective", listToAdd);
        }

        //delete existing role-user
        Map<String, Object> keyMap = new HashMap<String, Object>();
        keyMap.put(Const.TENANT_ID, tenantId);
        keyMap.put("roleId", roleId);
        ret += mDao.delete(Mapper.PRoleUserMapper, "deleteByRoleId", keyMap);
        //get all users in the role from role-member
        boolean roleInheritOrg = Platform.getBoolean("engine.role-inherit-org");
        List<PRoleUser> roleUserList = mDao.selectList(Mapper.PRoleUserMapper,
                roleInheritOrg ? "selectByRoleIdFromMemberInherit" : "selectByRoleIdFromMember",
                keyMap);
        //insert new role-user
        ret += mDao.batchInsert(Mapper.PRoleUserMapper, "insertSelective", roleUserList);
        rtn.put("result", ret);

        //更新缓存需要查询更新后用户成员
        getAffectedUsers(tenantId, roleId, logins);

        //更新缓存
        evictMenuCacheForUsers(tenantId, logins);

        //记录日志
        PRole role = new PRole();
        role.setTenantId(tenantId);
        role.setRoleId(roleId);
        role = mDao.selectOne(Mapper.PRoleMapper, "selectByObj", role);
        oplog.bizlog(tenantId, loginName, displayName, null, Oplog.OP.CHANGEMEMBER, Oplog.CODE.ROLE, role.getRoleName());

        return rtn;
    }

    @Override
    public String queryDhx(String tenantId, String queryParams, int fInd, int lInd, int timezoneOffset, String lang, String ordercolumns) {
        log.debug("queryDhx queryParams="+queryParams);
        Map<String, Object> params = new HashMap<String,Object>();
        Map<String, Object> bo = new HashMap<String,Object>();
        if (tenantId!=null) {
            bo.put(Const.TENANT_ID, tenantId);
        }
        if (queryParams!=null) {
            bo.putAll(JSONObject.parseObject(queryParams));
        }
        params.put("bo", bo);
        params.put("fromRow", fInd-1);
        params.put("toRow", lInd);
        params.put("rows", lInd - fInd + 1);
        params.put("timezoneOffset", timezoneOffset);
        params.put("orderby", DhxUtil.parseGridOrderBy(ordercolumns));
        log.debug("query params="+params);
        List<Map<String,?>> list = mDao.selectList(Mapper.PRoleMapper, "pagingSelect", params);
        int total = mDao.selectOne(Mapper.PRoleMapper, "pagingSelectCount", params);
        String[] cols = new String[]{"ROLE_ID", "ROLE_NAME", "ROLE_DESC", "CREATOR", "CREATE_TIME",
                "UPDATOR", "UPDATE_TIME"};
        String rtn= DhxUtil.toGridJsonStr(list,
                cols, fInd, total, timezoneOffset, lang);
        log.debug("queryDhx returns:\n"+rtn);
        return rtn;
    }

    protected void getAffectedUsers(String tenantId, String roleId, Set<String> rtn) {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put(Const.TENANT_ID, tenantId);
        paramMap.put("roleId", roleId);
        List<PRoleUser> list = mDao.selectList(Mapper.PRoleUserMapper, "selectByRoleId", paramMap);
        for (PRoleUser ru : list) {
            rtn.add(ru.getUserId());
        }
    }

    @Async
    protected void evictMenuCacheForUsers(String tenantId, Set<String> logins) {
        for (String loginName : logins) {
            cacheService.remove(Caches.USERRIGHTS, cacheService.asKey(Caches.UserRightsPrefix.MENUS_ADMIN.name(), tenantId, loginName));
            cacheService.remove(Caches.USERRIGHTS, cacheService.asKey(Caches.UserRightsPrefix.MENUS_PORTAL.name(), tenantId, loginName));
        }
    }

}
